package battlecruiser.board;

import battlecruiser.board.cell.Cell;
import battlecruiser.board.cell.CellStatus;
import battlecruiser.board.cell.coordinate.Coordinate;
import battlecruiser.board.cell.coordinate.Horizontals;
import battlecruiser.board.cell.coordinate.Verticals;
import battlecruiser.ship.placement.ship.Ship;
import battlecruiser.ship.placement.ship.ShipDirection;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author Yakiv Tymoshenko
 * @since 01.09.12
 */
public abstract class Board {

    private static Logger LOG = Logger.getLogger(Board.class);

    private static final int WIDTH = 10;
    private static final int HEIGHT = 10;

    public static boolean isWithinTheBoard(Coordinate coordinate) {
        int i = coordinate.getI();
        int j = coordinate.getJ();
        return ((i >= 0) && (i < HEIGHT)) && ((j >= 0) && (j < WIDTH));
    }

    public static int getHeight() {
        return HEIGHT;
    }

    public static int getWidth() {
        return WIDTH;
    }

    public static List<Coordinate> locateShipBorderWaters(Ship ship) {
        List<Coordinate> borders = new ArrayList<Coordinate>();

        if (ship.getDirection() == ShipDirection.HORIZONTAL) {
            int i = ship.getFirstDeckCoordinate().getI();
            for (int j = ship.getFirstDeckCoordinate().getJ(); j <= ship.getLastDeckCoordinate().getJ(); j++) {
                borders.add(new Coordinate(i + 1, j));      // lower
                borders.add(new Coordinate(i - 1, j));      // upper
            }
            int jLeft = ship.getFirstDeckCoordinate().getJ() - 1;
            int jRight = ship.getLastDeckCoordinate().getJ() + 1;
            for (int y = i - 1; y <= i + 1; y++) {            // includes diagonal cells
                borders.add(new Coordinate(y, jLeft));        // left
                borders.add(new Coordinate(y, jRight));       // right
            }
        } else {
            int j = ship.getFirstDeckCoordinate().getJ();
            for (int i = ship.getFirstDeckCoordinate().getI(); i <= ship.getLastDeckCoordinate().getI(); i++) {
                borders.add(new Coordinate(i, j - 1));      // left
                borders.add(new Coordinate(i, j + 1));      // right
            }
            int iUpper = ship.getFirstDeckCoordinate().getI() - 1;
            int iLower = ship.getLastDeckCoordinate().getI() + 1;
            for (int x = j - 1; x <= j + 1; x++) {
                borders.add(new Coordinate(iLower, x));     // lower
                borders.add(new Coordinate(iUpper, x));     // upper
            }

        }
        borders = excludeOutOfBoundsCoordinates(borders);

        return borders;
    }

    protected static Cell[][] initCells() {
        Cell[][] result = new Cell[HEIGHT][WIDTH];
        for (int i = 0; i < HEIGHT; i++) {
            for (int j = 0; j < WIDTH; j++) {
                result[i][j] = new Cell(new Coordinate(i, j), CellStatus.FREE);
            }
        }
        return result;
    }

    protected static List<String> getStatuses(String gameBoardAsString) {
        List<String> result = new ArrayList<String>();
        List<String> rows = readRows(gameBoardAsString);
        String rowWithLeftHeaderRemoved;
        List<String> rowValues;
        final int INDEX_OF_THE_FIRST_VALUE_IN_A_ROW = 3;
        for (String row : rows) {
            rowWithLeftHeaderRemoved = row.substring(INDEX_OF_THE_FIRST_VALUE_IN_A_ROW);
            rowValues = Arrays.asList(rowWithLeftHeaderRemoved.split(" "));
            for (String statusAsString : rowValues) {
                result.add(statusAsString);
            }
        }
        return result;
    }

    private static List<Coordinate> excludeOutOfBoundsCoordinates(List<Coordinate> coordinates) {
        List<Coordinate> result = new ArrayList<Coordinate>(coordinates);
        for (Coordinate coordinate : coordinates) {
            if (!isWithinTheBoard(coordinate)) {
                result.remove(coordinate);
            }
        }
        return result;
    }

    private static List<String> readRows(String gameBoardAsString) {
        final int SymbolAfterUpperHeaderIndex = 24;
        String boardWithUpperHeaderRemoved = gameBoardAsString.substring(SymbolAfterUpperHeaderIndex);
        return Arrays.asList(boardWithUpperHeaderRemoved.split("\n"));
    }


    protected Cell[][] cells;

    protected Board() {
        this.cells = initCells();
    }


    public void drawShipBorderWaters(Ship ship) {
        List<Coordinate> borders = locateShipBorderWaters(ship);
        for (Coordinate coordinate : borders) {
            cells[coordinate.getI()][coordinate.getJ()].setStatus(CellStatus.BORDER_WATERS);
        }
    }

    public void drawHit(Coordinate c) {
        LOG.debug("Hitting " + c);
        cells[c.getI()][c.getJ()].setStatus(CellStatus.DAMAGED);
    }

    public void drawMissed(Coordinate c) {
        cells[c.getI()][c.getJ()].setStatus(CellStatus.MISSED);
    }

    public void drawDestroyed(Ship ship) {
        LOG.debug("Destroying ship: " + ship);
        //Mark ship decks as destroyed
        for (Coordinate c : ship.getCoordinates()) {
            cells[c.getI()][c.getJ()].setStatus(CellStatus.DESTROYED);
        }
    }

    public boolean isDestroyed(Ship ship) {
        boolean result = true;
        for (Coordinate coordinate : ship.getCoordinates()) {
            if (!(cells[coordinate.getI()][coordinate.getJ()].getStatus() == CellStatus.DAMAGED ||
                    cells[coordinate.getI()][coordinate.getJ()].getStatus() == CellStatus.DESTROYED)) {
                result = false;
                break;
            }
        }
        return result;
    }

    public boolean isEmpty() {
        for (int i = 0; i < WIDTH; i++) {
            for (int j = 0; j < HEIGHT; j++) {
                if (cells[i][j].getStatus() != CellStatus.FREE) {
                    return false;
                }
            }
        }
        return true;
    }

    public List<Cell> getFreeCells() {
        List<Cell> result = new ArrayList<Cell>(WIDTH * HEIGHT);
        for (Cell cell : getCellList()) {
            if (cell.getStatus() == CellStatus.FREE) {
                result.add(cell);
            }
        }
        return result;
    }

    public List<Coordinate> getFreeCoordinates() {
        List<Coordinate> result = new ArrayList<Coordinate>(HEIGHT * WIDTH);
        for (Cell cell : getFreeCells()) {
            result.add(cell.getCoordinate());
        }
        return result;
    }

    public List<Cell> getCellList() {
        List<Cell> result = new ArrayList<Cell>(WIDTH * HEIGHT);
        for (int i = 0; i < HEIGHT; i++) {
            result.addAll(Arrays.asList(cells[i]).subList(0, WIDTH));
        }
        return result;
    }

    public Cell[][] getCells() {
        return cells;
    }


    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        final Horizontals[] HORIZONTALS = Horizontals.values();
        final Verticals[] VERTICALS = Verticals.values();
        stringBuilder.append("   ");
        for (Verticals vertical : VERTICALS) {
            stringBuilder.append(vertical);
            stringBuilder.append(" ");
        }
        for (int i = 0; i < HEIGHT; i++) {
            stringBuilder.append("\n");
            stringBuilder.append(String.format("%2s ", HORIZONTALS[i]));
            for (int j = 0; j < WIDTH; j++) {
                stringBuilder.append(cells[i][j]);
                stringBuilder.append(" ");
            }
        }
        stringBuilder.append("\n");
        return stringBuilder.toString();
    }
}
